using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using BoOnce.Components;
using BoOnce.Core.Components;
using BoOnce.Core;

namespace BoOnce.Steps
{
    class Credits : Step
    {
        #region Generic Attributes
        private Game _Game;
        private GraphicsDevice _GraphicsDevice;
        private InputComponent _Input;
        #endregion

        #region Cylinder Attributes
        private const int NB_VERTICES = 2 * 50;
        private short[] _TriangleStripIndices = new short[NB_VERTICES + 2];
        private Matrix _WorldMatrix;
        private Texture2D _Texture;
        private BasicEffect _BasciEffect;
        private VertexPositionNormalTexture[] _Vertices;
        private float _CylinderRot = 0.0f;
        #endregion

        #region Credit Text Attributes
        private const float SPACE_BETWEEN_TEXT = 10f;
        private const float SPACE_BETWEEN_TITLE = 20f;
        private const float TEXT_POSITION_BEGIN = 200f;
        private float _TextPosition = TEXT_POSITION_BEGIN;
        private LinkedList<CreditTextDescription> _CreditsText;
        private float _CreditTextHeight;
        private SpriteBatch _SpriteBatch;
        private SpriteFont _FontTitle;
        private SpriteFont _FontText;
        #endregion

        private class CreditTextDescription
        {
            public readonly String Text;
            public readonly Boolean IsTitle;

            public CreditTextDescription(String text, Boolean isTitle)
            {
                Text = text;
                IsTitle = isTitle;
            }
        }

        public Credits(Game game)
            : base(game, "Credits")
        {
            _Game = game;
            _GraphicsDevice = _Game.GraphicsDevice;
            _SpriteBatch = new SpriteBatch(_GraphicsDevice);
            _Input = (InputComponent)_Game.Services.GetService(typeof(InputComponent));
            InitCylinder();
            InitText();
        }

        private void InitText()
        {
            _CreditsText = new LinkedList<CreditTextDescription>();
            _CreditsText.AddLast(new CreditTextDescription("Credit", true));
            _CreditsText.AddLast(new CreditTextDescription("======", false));
            _CreditsText.AddLast(new CreditTextDescription("", false));
            _CreditsText.AddLast(new CreditTextDescription("Developpers:", true));
            _CreditsText.AddLast(new CreditTextDescription("Guillaume 'mcy' Vaudaux-Ruth", false));
            _CreditsText.AddLast(new CreditTextDescription("...", false));
            _CreditsText.AddLast(new CreditTextDescription("", false));
            _CreditsText.AddLast(new CreditTextDescription("Developpers:", true));
            _CreditsText.AddLast(new CreditTextDescription("Guillaume 'mcy' Vaudaux-Ruth", false));
            _CreditsText.AddLast(new CreditTextDescription("...", false));
            _CreditsText.AddLast(new CreditTextDescription("", false));
            _CreditsText.AddLast(new CreditTextDescription("Developpers:", true));
            _CreditsText.AddLast(new CreditTextDescription("Guillaume 'mcy' Vaudaux-Ruth", false));
            _CreditsText.AddLast(new CreditTextDescription("...", false));

            _CreditTextHeight = 0f;
            foreach (CreditTextDescription text in _CreditsText)
            {
                if (text.IsTitle) _CreditTextHeight += SPACE_BETWEEN_TITLE;
                else _CreditTextHeight += SPACE_BETWEEN_TEXT;
            }
        }

        private void InitCylinder()
        {
            for (int j = 0; j < NB_VERTICES; j++) _TriangleStripIndices[j] = (short)(j);
            // close the cylinder 
            _TriangleStripIndices[NB_VERTICES] = 0;
            _TriangleStripIndices[NB_VERTICES + 1] = 1;

            _Vertices = new VertexPositionNormalTexture[NB_VERTICES];
            float t = 0f;
            for (int i = 0; i < NB_VERTICES; i += 2)
            {
                float z = (float)Math.Sin((double)t);
                float y = (float)Math.Cos((double)t);
                _Vertices[i] = new VertexPositionNormalTexture(
                        new Vector3(0.0f, y, z),
                        Vector3.Forward,
                        new Vector2(0f, t / (2f * (float)Math.PI))
                        );
                _Vertices[i + 1] = new VertexPositionNormalTexture(
                        new Vector3(1.0f, y, z),
                        Vector3.Forward,
                        new Vector2(1f, t / (2f * (float)Math.PI))
                        );

                t += 2.0f * (float)Math.PI / (NB_VERTICES / 2);
            }

            _WorldMatrix = Matrix.CreateTranslation(new Vector3(-0.5f, -0.6f, 0.0f)) *
                Matrix.CreateScale(2.3f);

            Matrix viewMatrix = Matrix.CreateLookAt(
                new Vector3(0.0f, 0.0f, 4.0f),
                new Vector3(0.0f, 0.0f, 0.0f),
                Vector3.Up
                );

            Matrix projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.ToRadians(45),
                (float)_GraphicsDevice.Viewport.Width /
                (float)_GraphicsDevice.Viewport.Height,
                1.0f, 100.0f
                );

            _BasciEffect = new BasicEffect(_GraphicsDevice, null);
            _BasciEffect.World = _WorldMatrix;
            _BasciEffect.View = viewMatrix;
            _BasciEffect.Projection = projectionMatrix;
            _BasciEffect.TextureEnabled = true;
            _Texture = Game.Content.Load<Texture2D>(Settings.CONTENT_LEVEL_PATH + "/bus/ground1");
            _BasciEffect.Texture = _Texture;
        }

        public override void LoadContent()
        {
            _FontText = _Game.Content.Load<SpriteFont>(Settings.CONTENT_FONT_PATH + "/Arial8");
            _FontTitle = _Game.Content.Load<SpriteFont>(Settings.CONTENT_FONT_PATH + "/Comic14");
            base.LoadContent();
        }

        public override void Draw(GameTime gameTime)
        {
            _GraphicsDevice.Clear(Color.DarkBlue);
            float PositionDecal = 0.0f;
            _SpriteBatch.Begin();
            foreach (CreditTextDescription text in _CreditsText)
            {
                if (text.IsTitle)
                {
                    _SpriteBatch.DrawString(_FontTitle, text.Text, new Vector2(200f, _TextPosition + PositionDecal), Color.Red);
                    PositionDecal += SPACE_BETWEEN_TITLE;
                }
                else
                {
                    _SpriteBatch.DrawString(_FontText, text.Text, new Vector2(200f, _TextPosition + PositionDecal), Color.Black);
                    PositionDecal += SPACE_BETWEEN_TEXT;
                }
            }
            _SpriteBatch.End();

            _BasciEffect.Begin();
            foreach (EffectPass pass in _BasciEffect.CurrentTechnique.Passes)
            {
                pass.Begin();
                VertexDeclaration basicEffectVertexDeclaration =
                    new VertexDeclaration(_GraphicsDevice, VertexPositionNormalTexture.VertexElements);

                _GraphicsDevice.VertexDeclaration = basicEffectVertexDeclaration;
                _GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(
                    PrimitiveType.TriangleStrip,
                    _Vertices,
                    0,  // vertex buffer offset to add to each element of the index buffer
                    NB_VERTICES,  // number of vertices to draw
                    _TriangleStripIndices,
                    0,  // first index element to read
                    NB_VERTICES   // number of primitives to draw
                );
                pass.End();
            }
            _BasciEffect.End();
            base.Draw(gameTime);
        }

        public override void Update(GameTime gameTime)
        {
            float timeElasped = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
            _CylinderRot += timeElasped * 0.004f;
            _BasciEffect.World = Matrix.CreateRotationX(_CylinderRot) *
                Matrix.CreateTranslation(new Vector3(-0.5f, -0.9f, 0.0f)) *
                Matrix.CreateScale(2.3f);

            _TextPosition -= timeElasped * 0.03f;

            if (_TextPosition + _CreditTextHeight < -10f) _TextPosition = TEXT_POSITION_BEGIN;
            base.Update(gameTime);
        }
    }
}
